package cn.dengta.webapp.base.service;

import java.io.Serializable;
import java.util.*;
import javax.annotation.Nonnull;

import cn.dengta.common.model.Bean;
import cn.dengta.common.model.ID;
import cn.dengta.common.web.Page;
import cn.dengta.webapp.base.dao.BaseDao;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import me.codeplayer.util.Assert;
import me.codeplayer.util.X;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public abstract class BaseServiceImpl<T extends Bean<K>, D extends BaseDao<T>, K extends Serializable> implements BaseService<T, K> {

	protected D baseDao;

	protected D getBaseDao() {
		return baseDao;
	}

	@Autowired
	public void setBaseDao(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") D baseDao) {
		this.baseDao = baseDao;
	}

	protected static void checkIdRequired(ID entity) {
		Assert.isTrue(ID.hasId(entity));
	}

	@Override
	public T get(K id) {
		if (id == null) {
			return null;
		}
		return baseDao.selectById(id);
	}

	@Override
	public T getAny() {
		return getBaseDao().selectOne(new QueryWrapper<T>().last("LIMIT 1"));
	}

	@Override
	public T getByIdForUpdate(K id) {
		if (id == null) {
			return null;
		}
		QueryWrapper<T> wrapper = new QueryWrapper<>();
		wrapper.eq("id", id).last(" FOR UPDATE");

		return baseDao.selectOne(wrapper);
	}

	/**
	 * 根据主键ID数组获取对应的实体数据集合
	 */
	@Override
	public List<T> findByIds(K... ids) {
		return findByIds(Arrays.asList(ids));
	}

	/**
	 * 根据主键ID集合获取对应的实体数据集合
	 */
	@Override
	public List<T> findByIds(Collection<K> ids) {
		if (ids.isEmpty()) {
			return new ArrayList<>();
		}
		return baseDao.selectBatchIds(ids);
	}

	protected List<T> findByField(String field, Object value) {
		QueryWrapper<T> wrapper = new QueryWrapper<T>()
				.eq(field, value);
		return baseDao.selectList(wrapper);
	}

	/**
	 * 根据指定的字段获取对应的唯一的持久化实例。<br>
	 * 如果没有对应的实例，则返回 null。
	 */
	protected T getUniqueByField(String uniqueField, Object value) {
		QueryWrapper<T> wrapper = new QueryWrapper<T>()
				.eq(uniqueField, value);
		return baseDao.selectOne(wrapper);
	}

	/**
	 * 根据 用户ID 查询关联的实体对象
	 */
	public T getByUserId(@Nonnull Long userId) {
		Assert.notNull(userId);
		QueryWrapper<T> wrapper = new QueryWrapper<T>()
				.eq("user_id", userId);
		return baseDao.selectOne(wrapper);
	}

	/**
	 * 获取指定实体类的所有实体对象的集合，并以指定的方式进行排序
	 */
	public List<T> findAll() {
		return baseDao.selectList(null);
	}

	/**
	 * 添加指定的对象(到对应的数据库表中)
	 */
	public void save(T entity) {
		baseDao.insert(entity);
	}

	/**
	 * 更新指定的对象
	 */
	public int update(T entity) {
		return baseDao.updateById(entity);
	}

	/**
	 * 更新指定的对象
	 */
	public int update(T entity, Wrapper<T> updateWrapper) {
		return baseDao.update(entity, updateWrapper);
	}

	/**
	 * 添加、更新指定的对象
	 */
	protected void saveOrUpdate(T entity, boolean hasId) {
		if (hasId) {
			update(entity);
			return;
		}
		save(entity);
	}

	/**
	 * 添加、更新指定的对象【设置空值使用】
	 *
	 * @param entity 实体对象
	 * @param hasId 是否编辑
	 * @param fieldAndValues 修改的属性【存在相同属性，以该属性为主】
	 */
	protected void saveOrUpdate(T entity, boolean hasId, Object... fieldAndValues) {
		if (hasId) {
			UpdateWrapper<T> wrapper = new UpdateWrapper<>();
			wrapper.eq("id", entity.getId());
			if (X.isValid(fieldAndValues)) {
				for (int i = 0; i < fieldAndValues.length; i++) {
					wrapper.set((String) fieldAndValues[i++], fieldAndValues[i]);
				}
			}
			update(entity, wrapper);
			return;
		}
		save(entity);
	}

	/**
	 * 根据条件查询对应的实体数据
	 */
	protected T selectOne(Wrapper<T> queryWrapper) {
		return baseDao.selectOne(queryWrapper);
	}

	/**
	 * 根据条件查询对应的实体数
	 */
	protected Long count(Wrapper<T> queryWrapper) {
		return baseDao.selectCount(queryWrapper);
	}

	/**
	 * 根据条件查询对应的实体数据
	 */
	protected List<T> selectList(Wrapper<T> queryWrapper) {
		return baseDao.selectList(queryWrapper);
	}

	/**
	 * 根据条件查询对应的实体数据并分页
	 */
	protected Page<T> selectPage(Page<?> page, Wrapper<T> queryWrapper) {
		if (page.getSize() < 0) {
			List<T> list = baseDao.selectList(queryWrapper);
			int size = X.size(list);
			return new Page<T>(1, size, size).setList(list);
		}
		return baseDao.selectPage(X.castType(page), queryWrapper);
	}

	/**
	 * 从数据存储中移除指定的实体对象
	 */
	public boolean delete(T entity) {
		Assert.notNull(Bean.idOf(entity));
		return deleteById(entity.getId());
	}

	/**
	 * 从数据存储中移除指定的实体对象
	 */
	public boolean deleteById(K id) {
		return baseDao.deleteById(id) > 0;
	}

	/**
	 * 从数据存储中批量移除指定的实体对象
	 */
	public int deleteByIds(K... ids) {
		return deleteByIds(Arrays.asList(ids));
	}

	/**
	 * 从数据存储中批量移除指定的实体对象
	 */
	public int deleteByIds(Collection<K> ids) {
		return baseDao.deleteBatchIds(ids);
	}

	/**
	 * 从数据存储中移除符合指定条件的数据
	 */
	protected int delete(UpdateWrapper<T> wrapper) {
		return baseDao.delete(wrapper);
	}

	/**
	 * 更新符合指定条件的数据
	 */
	protected int update(UpdateWrapper<T> wrapper) {
		Assert.notNull(wrapper);
		return baseDao.update(null, wrapper);
	}

	/**
	 * 更新符合指定条件的数据
	 */
	protected int update(T toUpdate, UpdateWrapper<T> wrapper) {
		Assert.isTrue(toUpdate != null || wrapper != null);
		return baseDao.update(toUpdate, wrapper);
	}

	/**
	 * 判断对象表中是否存在指定属性的值
	 *
	 * @param fieldAndValues 键值对(键必须为字符串)
	 * @since 1.0
	 */
	@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
	public boolean exists(Object... fieldAndValues) {
		QueryWrapper<T> wrapper = new QueryWrapper<>();
		for (int i = 0; i < fieldAndValues.length; i++) {
			wrapper.eq((String) fieldAndValues[i++], fieldAndValues[i]);
		}
		return baseDao.exists(wrapper);
	}

	public int updateHandleStatus(Integer oldStatus, Integer status, Long... ids) {
		return update(null, new UpdateWrapper<T>()
				.set("handle_status", status)
				.in("id", (Object[]) ids)
				.eq("handle_status", oldStatus)
		);
	}

}
